#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/poll.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <openssl/sha.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>

#define ENABLE_HTTP_RESPONSE 1

#define ROOT_DIR "/home/luowailin/Documents/network_c"

#define BUFFER_LENGTH 1024

typedef int (*RCALLBACK)(int fd);

int accept_cb(int fd);
int recv_cb(int fd);
int send_cb(int fd);

struct conn_item {
    int fd;

    char rbuffer[BUFFER_LENGTH];
    int rlen;
    char wbuffer[BUFFER_LENGTH];
    int wlen;
   union {
        RCALLBACK accept_callback;
        RCALLBACK recv_callback;
    } recv_t;
    RCALLBACK send_callback;
};
struct conn_item connlists[1024] = {0};
int epfd = 0;
#if 0
struct reactor {
    int epfd;
    struct conn_item *connlist;
};
#else

#endif

#if ENABLE_HTTP_RESPONSE
typedef struct conn_item connection_t;
int http_request(connection_t *conn) {

}

int http_response(connection_t *conn){
#if 0
    conn->wlen = sprintf(conn->wbuffer, 
    "HTTP/1.1 200 OK\r\n"
    "Accept-Ranges: bytes\r\n"
    "Content-Length: 78\r\n"
    "Content-Type: text/html\r\n"
    "Date: Web, 04 Sep 2024 13:14:46 GMT\r\n\r\n"
    "<html><head><title>0voice.king</title></head><body><h1>King</h1></body></html>\r\n\n");
    return conn->wlen;
#else
    int filefd = open("index.html", O_RDONLY);
    struct stat stat_buf;
    fstat(filefd, &stat_buf);

    conn->wlen = sprintf(conn->wbuffer, 
        "HTTP/1.1 200 OK\r\n"
        "Accept-Ranges: bytes\r\n"
        "Content-Length: %ld\r\n"
        "Content-Type: text/html\r\n"
        "Date: Web, 04 Sep 2024 13:14:46 GMT\r\n\r\n", stat_buf.st_size);

    int count = read(filefd, conn->wbuffer + conn->wlen, BUFFER_LENGTH - conn->wlen);
    conn->wlen += count;
    close(filefd);
    return conn->wlen;
#endif
    
}
#endif

//// base64 
char *base64_encode(const unsigned char *input, int length) {
    BIO *bmem, *b64;
    BUF_MEM *bptr;

    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new(BIO_s_mem());
    b64 = BIO_push(b64, bmem);

    BIO_write(b64, input, length);
    BIO_flush(b64);
    BIO_get_mem_ptr(b64, &bptr);

    char *buff = (char *)malloc(bptr->length + 1);
    memcpy(buff, bptr->data, bptr->length);
    buff[bptr->length] = 0;
    
    BIO_free_all(b64);
    
    return buff;
}

char* get_header_value(const char* request, const char* header_name, char* value, size_t value_size) {
    char* header_start = strstr(request, header_name);
    if (header_start) {
        header_start += strlen(header_name);
        while (*header_start == ' ') header_start++;  // Skip spaces

        char* header_end = strstr(header_start, "\r\n");
        if (header_end) {
            size_t len = header_end - header_start;
            if (len < value_size) {
                strncpy(value, header_start, len);
                value[len] = '\0';
                return value;
            }
        }
    }
    return NULL;
}

int set_event(int fd, int event, int flag) {
    struct epoll_event ev;
    ev.events = event;
    ev.data.fd = fd;
    if (flag == 1) { //add
        epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
    } else {
        epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
    }
    return 0;
}

void ws_handshake(int fd, const char* request) {
    char key[256];
    if (get_header_value(request, "Sec-WebSocket-Key:", key, sizeof(key)) == NULL) {
        return;
    }
    char *accept_key = NULL;
    char hashed_key[SHA_DIGEST_LENGTH];
    char combined_key[512];

    snprintf(combined_key, sizeof(combined_key), "%s%s", key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
    SHA1((unsigned char*)combined_key, strlen(combined_key), (unsigned char *)hashed_key);
    // base64编码
    accept_key = base64_encode(hashed_key, SHA_DIGEST_LENGTH);

    const char *response_template = "HTTP/1.1 101 Switching Protocols\r\n"
                                    "Upgrade: websocket\r\n"
                                    "Connection: Upgrade\r\n"
                                    "Sec-WebSocket-Accept: %s\r\r\n\r\n";
    char response[512];
    memset(response, 0x0, 512);
    snprintf(response, sizeof(response), response_template, accept_key);
    send(fd, response, strlen(response), 0);
    free(accept_key);
    accept_key = NULL;
    set_event(fd, EPOLLOUT, 0);
}

int accept_cb(int fd) {
    printf("accept_cb.......\n");
    struct sockaddr_in clientaddr;
    socklen_t len = sizeof(clientaddr);
    int clientfd = accept(fd, (struct sockaddr *)&clientaddr, &len);
    if (clientfd < 0) {
        return -1;
    }

    set_event(clientfd, EPOLLIN, 1);

    connlists[clientfd].fd = clientfd;
    memset(connlists[clientfd].rbuffer, 0, BUFFER_LENGTH);
    connlists[clientfd].rlen = 0;
    memset(connlists[clientfd].wbuffer, 0, BUFFER_LENGTH);
    connlists[clientfd].wlen = 0;

    connlists[clientfd].recv_t.recv_callback = recv_cb;
    connlists[clientfd].send_callback = send_cb;
    return clientfd;
}

int recv_cb(int fd) {
    char *buffer = connlists[fd].rbuffer;
    printf("recv buffer:%s\n", buffer);
    int idx = connlists[fd].rlen;
    int count = recv(fd, buffer + idx, BUFFER_LENGTH - idx, 0);
    if (count == 0) {
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        close(fd);
        return -1;
    }
    connlists[fd].rlen += count;
    if (strstr(buffer, "GET / HTTP/1.1") != NULL) {
        ws_handshake(fd, buffer);
        memset(connlists[fd].rbuffer, 0, BUFFER_LENGTH);
        connlists[fd].rlen = 0;
        return count;
    }

    unsigned char *data = (unsigned char *)buffer;
    int opcode = data[0] & 0x0F;
    if (opcode == 0x8) { // Close frame
        printf("Close frame received\n");
        epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
        close(fd);
        return -1;
    } else if (opcode == 0x9) { // Ping frame
        printf("Ping frame received\n");
        return 0;
    } else if (opcode != 0x1) { // Check for text frame
        printf("Unknown opcode: %d\n", opcode);
        return -1;
    }

    int payload_len = data[1] & 0x7F;
    int mask_index = 2;
    if (payload_len == 126) {
        mask_index = 4;
    } else if (payload_len == 127) {
        mask_index = 10;
    }

    if (connlists[fd].rlen < mask_index + 4 + payload_len) {
        // Not enough data received, wait for more
        return count;
    }

    unsigned char mask[4];
    memcpy(mask, data + mask_index, 4);

    int data_index = mask_index + 4;
    for (int i = 0; i < payload_len; i++) {
        data[data_index + i] ^= mask[i % 4];
    }

    memcpy(connlists[fd].wbuffer, data + data_index, payload_len);
    connlists[fd].wlen = payload_len;
    set_event(fd, EPOLLOUT, 0);

    memset(connlists[fd].rbuffer, 0, BUFFER_LENGTH);
    connlists[fd].rlen = 0;
    return count;
}

int send_cb(int fd) {
    /*
    char *buffer = connlists[fd].wbuffer;
    int idx = connlists[fd].wlen;
    int count = send(fd, buffer, idx, 0);
    memset(connlists[fd].wbuffer, 0, BUFFER_LENGTH);
    connlists[fd].wlen = 0;
    set_event(fd, EPOLLIN, 0);
    return count;*/

    char *buffer = connlists[fd].wbuffer;
    int payload_len = connlists[fd].wlen;
    unsigned char frame[BUFFER_LENGTH];
    int frame_size = 2;

    frame[0] = 0x81;
    if (payload_len <= 125) {
        frame[1] = payload_len;
    } else if (payload_len <= 65535) {
        frame[1] = 126;
        frame[2] = (payload_len >> 8 ) & 0xFF;
        frame[3] = payload_len & 0xFF;
        frame_size = 4;
    } else {
        frame[1] = 127;
    }

    memcpy(frame + frame_size, buffer, payload_len);
    frame_size += payload_len;

    send(fd, frame, frame_size, 0);
    memset(connlists[fd].wbuffer, 0, BUFFER_LENGTH);
    connlists[fd].wlen = 0;
    set_event(fd, EPOLLIN, 0);
    return frame_size;
}

int main() {
    // 0 stdin 1 stdout 2 stderr 
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    
    struct sockaddr_in serveraddr;
    memset(&serveraddr, 0, sizeof(struct sockaddr_in));

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
    serveraddr.sin_port = htons(2048);

    if (bind(socketfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)) == -1) {
        perror("bind");
        return -1;
    }
    listen(socketfd, 10);
    connlists[socketfd].fd = socketfd;
    connlists[socketfd].recv_t.accept_callback = accept_cb;

    epfd = epoll_create(1);
    set_event(socketfd, EPOLLIN, 1);
    struct epoll_event events[1024] = {0};

    while (1) {
        int nready = epoll_wait(epfd, events, 1024, -1);

        int i = 0;
        for (i = 0; i < nready; i ++) {
            int connectfd = events[i].data.fd;
            if (events[i].events & EPOLLIN) {
                int count = connlists[connectfd].recv_t.recv_callback(connectfd);
                // printf("recv count:%d <-----buffer:%s\n", count, connlists[connectfd].rbuffer);
            } else if (events[i].events & EPOLLOUT) {
                int count =  connlists[connectfd].send_callback(connectfd);
                // printf("send ---> buffer:%s\n", connlists[connectfd].wbuffer);
            } 
        }
    }
    getchar();
    close(socketfd);
    return 0;
}